﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom.Compiler;
using System.IO;
using System.CodeDom;
using DotNetDalAddin.Properties;
using System.Data;

namespace DotNetDalAddin.CodeHelper
{
    public class DataCodeTable
    {
        private CodeNamespace codeNs = new CodeNamespace();
        private DotNetDal.Core.Model.Table table = new DotNetDal.Core.Model.Table();

        public DataCodeTable(DotNetDal.Core.Model.Table t)
        {
            table = t;

            GenerateClass();
        }

        public string GenerateCode(string nameSpace)
        {
            CodeGeneratorOptions cop = new CodeGeneratorOptions();

            cop.BracingStyle = "C";
            cop.IndentString = "    ";

            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);

            ICodeGenerator cg = new Microsoft.CSharp.CSharpCodeProvider().CreateGenerator();

            cg.GenerateCodeFromNamespace(InitializeNameSpace(nameSpace), sw, cop);

            return sb.ToString();
        }

        private void GenerateClass()
        {
            CodeTypeDeclaration ctd = new CodeTypeDeclaration("Abstract" + table.Name);

            if (table.Type == DotNetDal.Core.Model.Table.TableType.BaseTable)
                ctd.BaseTypes.Add(new CodeTypeReference("DALTable"));
            else
                ctd.BaseTypes.Add(new CodeTypeReference("DALView"));

            ctd.Attributes = MemberAttributes.Abstract;
            ctd.IsPartial = true;
            ctd.CustomAttributes.Add(new CodeAttributeDeclaration("TableAttribute", new CodeAttributeArgument[] { new CodeAttributeArgument("Schema", new CodePrimitiveExpression(table.Schema.Name)) }));

            codeNs.Types.Add(ctd);

            foreach (DotNetDal.Core.Model.Field f in table.Fields)
            {
                GenerateField(f, ctd);
            }

            GenerateInstance(ctd);
            GenerateGetFromSqlMethod(ctd);
        }
        private void GenerateGetFromSqlMethod(CodeTypeDeclaration ctd)
        {
            CodeMemberMethod meth = new CodeMemberMethod();

            // Creates a code expression for a CodeExpressionStatement to contain.
            CodeExpression invokeExpression = new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression("DALRecord"),
                "GetFromSql<" + Settings.Default.BusinessFolderName + "." + table.Name + ">", new CodeVariableReferenceExpression("sql"));

            // Creates a statement using a code expression.
            CodeMethodReturnStatement expressionStatement = new CodeMethodReturnStatement(invokeExpression);

            meth.Name = "GetFromSql";
            meth.ReturnType = new CodeTypeReference(Settings.Default.BusinessFolderName + "." + table.Name + "[]");
            meth.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            meth.Parameters.Add(new CodeParameterDeclarationExpression(Type.GetType("System.String"), "sql"));
            meth.Statements.Add(expressionStatement);

            ctd.Members.Add(meth);

            meth = new CodeMemberMethod();

            // Creates a code expression for a CodeExpressionStatement to contain.
            invokeExpression = new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression("DALRecord"),
                "GetFromSql<" + Settings.Default.BusinessFolderName + "." + table.Name + ">", new CodeVariableReferenceExpression("sql"), new CodeVariableReferenceExpression("sqlParams"));

            // Creates a statement using a code expression.
            expressionStatement = new CodeMethodReturnStatement(invokeExpression);

            meth.Name = "GetFromSql";
            meth.ReturnType = new CodeTypeReference(Settings.Default.BusinessFolderName + "." + table.Name + "[]");
            meth.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            meth.Parameters.Add(new CodeParameterDeclarationExpression(Type.GetType("System.String"), "sql"));
            meth.Parameters.Add(new CodeParameterDeclarationExpression("SqlParameter[]", "sqlParams"));
            meth.Parameters[1].CustomAttributes.Add(new CodeAttributeDeclaration("System.ParamArrayAttribute"));
            meth.Statements.Add(expressionStatement);

            ctd.Members.Add(meth);
        }
        private void GenerateField(DotNetDal.Core.Model.Field f, CodeTypeDeclaration ctd)
        {
            CodeMemberProperty property = new CodeMemberProperty();

            property.Name = f.Name;
            property.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            if (f.IsNullable && f.DotNetType != Type.GetType("System.String") && f.DotNetType != Type.GetType("System.Byte[]"))
            {
                property.Type = new CodeTypeReference(typeof(System.Nullable));
                property.Type.TypeArguments.Add(f.DotNetType);

                property.GetStatements.Add(new CodeMethodReturnStatement(
                    new CodeCastExpression(property.Type, new CodeArrayIndexerExpression(new CodeThisReferenceExpression(), new CodePrimitiveExpression(f.Name)))));
            }
            else
            {
                property.Type = new CodeTypeReference(f.DotNetType);

                property.GetStatements.Add(new CodeMethodReturnStatement(
                    new CodeCastExpression(new CodeTypeReference(f.DotNetType), new CodeArrayIndexerExpression(new CodeThisReferenceExpression(), new CodePrimitiveExpression(f.Name)))));
            }

            property.SetStatements.Add(
                new CodeAssignStatement(
                    new CodeArrayIndexerExpression(new CodeThisReferenceExpression(), new CodePrimitiveExpression(f.Name)),
                    new CodePropertySetValueReferenceExpression()));

            List<CodeAttributeArgument> attributeList = new List<CodeAttributeArgument>();

            //SqlDbType
            SqlDbType sdt = (SqlDbType)Enum.Parse(typeof(SqlDbType), f.SqlDataType, true);
            DbType dt = DotNetDal.Core.SqlServer.FromSqlDbType(sdt);

            attributeList.Add(new CodeAttributeArgument("DbType",
                new CodeFieldReferenceExpression(
                    new CodeTypeReferenceExpression(typeof(DbType)), dt.ToString())));

            //IsNullable
            attributeList.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression(f.IsNullable)));

            //IsPrimaryKey
            if (f.IsPrimaryKey)
                attributeList.Add(new CodeAttributeArgument("IsPrimaryKey", new CodePrimitiveExpression(true)));

            //IsAutoNumber
            if (f.IsIdentity)
                attributeList.Add(new CodeAttributeArgument("IsAutoNumber", new CodePrimitiveExpression(true)));

            property.CustomAttributes.Add(new CodeAttributeDeclaration("FieldAttribute", attributeList.ToArray()));

            ctd.Members.Add(property);
        }

        private void GenerateInstance(CodeTypeDeclaration ctd)
        {
            CodeMemberField cmf = new CodeMemberField(Settings.Default.BusinessFolderName + "." + table.Name, "instance");
            cmf.InitExpression = new CodeObjectCreateExpression(Settings.Default.BusinessFolderName + "." + table.Name);
            cmf.Attributes = MemberAttributes.Private | MemberAttributes.Static;

            ctd.Members.Add(cmf);
        }
        private CodeNamespace InitializeNameSpace(string name)
        {
            codeNs.Imports.Add(new CodeNamespaceImport("System"));
            codeNs.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
            codeNs.Imports.Add(new CodeNamespaceImport("System.Data"));
            codeNs.Imports.Add(new CodeNamespaceImport("System.Data.SqlClient"));
            codeNs.Imports.Add(new CodeNamespaceImport("DotNetDal.Core"));
            codeNs.Imports.Add(new CodeNamespaceImport("DotNetDal.Core.Data"));
            codeNs.Imports.Add(new CodeNamespaceImport("DotNetDal.Core.StructureAttributes"));
            codeNs.Name = name + "." + Settings.Default.AbstractFolderName;

            return codeNs;
        }
    }
}